using System.Collections;
using System.Collections.Generic;
using System.IO;

namespace Dojo {
    public class Copier {
        private string dojoHome;
        private Hashtable resolvedUrls;
        private const string DOJO = "dojo";

        public Copier(string dojoHome) {
            this.dojoHome = dojoHome;
        }

        public virtual void CopyTo(string destinationPath, Hashtable cache) {
            resolvedUrls = cache;
            DirectoryInfo destination = createTheDestinationFolder(destinationPath);

            IList<string> directoriesCreated = new List<string>();

            createDirectories(destination, directoriesCreated);
            removeDirectoryEntriesThatHaveBeenCreated(directoriesCreated);
            copyFiles(destination);

            copyMainDojoFile(destination);
        }

        private void copyMainDojoFile(DirectoryInfo destination) {
            FileInfo f = new FileInfo(dojoHome + Path.DirectorySeparatorChar + "dojo.js");
            f.CopyTo(destination.FullName + Path.DirectorySeparatorChar + DOJO + Path.DirectorySeparatorChar + f.Name, true);
        }

        private DirectoryInfo createTheDestinationFolder(string destinationPath) {
            DirectoryInfo destination = new DirectoryInfo(destinationPath);
            destination.Create();
            return destination;
        }

        private void copyFiles(DirectoryInfo destination) {
            foreach (object key in resolvedUrls.Keys) {
                ResolvedUrl url = (ResolvedUrl) resolvedUrls[key];
                FileInfo file = new FileInfo(getFullPathFromResolvePath(url));
                try {
                    file.CopyTo(getCorrespondingDestinationPath(destination, url), true);
                } catch (DirectoryNotFoundException d) {
                    destination.CreateSubdirectory(url.DirectoryPartOfFilePath);
                    file.CopyTo(getCorrespondingDestinationPath(destination, url), true);
                    CopyPackageInfo(destination, url);
                }
            }
        }

        private void CopyPackageInfo(DirectoryInfo destination, ResolvedUrl url) {
            FileInfo f = new FileInfo(getFullPathOfPackageFile(url));
            if(f.Exists)
                f.CopyTo(destination.FullName + Path.DirectorySeparatorChar + url.DirectoryPartOfFilePath + Path.DirectorySeparatorChar + f.Name);
        }

        private string getFullPathOfPackageFile(ResolvedUrl url) {
            return getFullDirectoryPathOfResolvedFileUrl(url) + Path.DirectorySeparatorChar + "__package__.js";
        }

        private string getFullDirectoryPathOfResolvedFileUrl(ResolvedUrl url) {
            return url.DirectoryPartOfFilePath.Replace(DOJO, dojoHome);
        }

        private static string getCorrespondingDestinationPath(DirectoryInfo destination, ResolvedUrl url) {
            return destination.FullName + Path.DirectorySeparatorChar + url.DojoPath;
        }

        private void removeDirectoryEntriesThatHaveBeenCreated(IList<string> entriesToBeRemoved) {
            foreach (string entry in entriesToBeRemoved) {
                resolvedUrls.Remove(entry);
            }
        }

        private void createDirectories(DirectoryInfo destination, IList<string> entriesToBeRemoved) {
            foreach (object key in resolvedUrls.Keys) {
                ResolvedUrl url = (ResolvedUrl) resolvedUrls[key];
                IfDirectoryThenCreateTheDirectoryStructure(destination, entriesToBeRemoved, key, url);
            }
        }

        private void IfDirectoryThenCreateTheDirectoryStructure(DirectoryInfo destination, IList<string> entriesToBeRemoved, object key, ResolvedUrl url) {
            if (url.IsDirectory) {
                entriesToBeRemoved.Add((string) key);
                destination.CreateSubdirectory(url.DojoPath);
                copyAllFilesInTheDirectoryToTheDestinationDirectory(getFullPathFromResolvePath(url), destination, url);
            }
        }

        private string getFullPathFromResolvePath(ResolvedUrl url) {
            return url.DojoPath.Replace(DOJO, dojoHome);
        }

        private void copyAllFilesInTheDirectoryToTheDestinationDirectory(string path, DirectoryInfo destination, ResolvedUrl url) {
            DirectoryInfo resolvedDirectory = new DirectoryInfo(path);
            foreach (FileInfo file in resolvedDirectory.GetFiles()) {
                file.CopyTo(getCorrespondingDestinationPath(destination, file, url), true);
            }
        }

        private static string getCorrespondingDestinationPath(DirectoryInfo destination, FileInfo file, ResolvedUrl url) {
            return destination.FullName + Path.DirectorySeparatorChar + url.DojoPath + file.Name;
        }
    }
}